package dominion.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import dominion.model.actions.Action;
import dominion.model.points.Gardens;
import dominion.model.points.Points;
import dominion.utils.Constants;

public class Deck {

	private List<Card> deck;

	public Deck() {
		deck = new ArrayList<Card>();
	}

	public void shuffle() {
		Collections.shuffle(deck);
	}

	public void addToDeck(Card card) {
		List<Card> list = new ArrayList<Card>();
		list.add(card);
		addToDeck(list);
	}

	public void addToDeck(List<Card> list) {
		for (Card current : list)
		{
			deck.add(current);
		}
		list.clear();
	}

	/**
	 * Place card on top of deck (position 0)
	 * 
	 * @param card
	 *            - Card to be added
	 */
	public void placeOnDeck(Card card) {
		deck.add(0, card);
	}

	/**
	 * Draws the top X cards of the deck where X is given
	 * 
	 * @param cardsToDraw
	 *            - number of card to draw
	 * @return List<Card> of top cards
	 */
	public List<Card> drawCards(int cardsToDraw) {
		List<Card> list = new ArrayList<Card>();
		int i = 0;
		while (!deck.isEmpty() && i < cardsToDraw)
		{
			Card current = deck.remove(0);
			list.add(current);
			i++;
		}
		return list;
	}

	/**
	 * Returns the top card of the deck. Card IS removed because of this call.
	 * 
	 * @return Top card of the deck or null if deck is empty
	 */
	public Card drawTopCard() {
		Card card = null;
		if (deck.isEmpty())
		{
			System.out.println("WARNING: Deck is empty");
		}
		else
		{
			card = deck.remove(0);
		}
		return card;
	}

	/**
	 * Gets a card from the deck with the given name (ignores case). Card IS
	 * removed from deck as a result.
	 * 
	 * @param cardName
	 *            - name of card to be drawn
	 * @return - Card with given name, or null if it didn't exist
	 */
	public Card drawCard(String cardName) {
		Card card = Constants.cardMap.get(cardName.toLowerCase());
		if (card == null)
		{
			System.out.println("WARNING: trying to draw invalid card!");
			return null;
		}
		if (!contains(card))
		{
			System.out.println("WARNING: deck does not contain: " + cardName);
			return null;
		}
		else
		{
			card = getCard(cardName);
			deck.remove(card);
		}
		return card;
	}

	/**
	 * Returns top card of the deck. Card is not removed because of this call
	 * 
	 * @return - Reference to the deck's top card or null if deck is empty
	 */
	public Card revealTopCard() {
		Card card = null;
		if (deck.isEmpty())
		{
			System.out.println("WARNING: DECK IS EMPTY");
		}
		else
		{
			card = deck.get(0);
		}
		return card;
	}

	/**
	 * Gets a reference to a card with the given name (ignores case). Card
	 * remains in the deck even after caling this.
	 * 
	 * @param cardName
	 *            - name of the card
	 * @return Card with the given name
	 */
	public Card getCard(String cardName) {
		Card result = null;
		for (Card current : deck)
		{
			if (cardName.equalsIgnoreCase(current.toString()))
			{
				result = current;
				break;
			}
		}
		return result;
	}

	public void removeAll() {
		deck.clear();
	}

	public boolean isEmpty() {
		return deck.isEmpty();
	}

	public int size() {
		return deck.size();
	}

	public List<Card> getList() {
		return deck;
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		for (Card current : deck)
		{
			sb.append("|" + current.toString() + "|");
		}
		return sb.toString();
	}

	public boolean contains(Card card) {
		return deck.contains(card);
	}

	/**
	 * Checks if deck has a card with a given name (ignoring case)
	 * 
	 * @param card
	 *            - card name
	 * @return - true if card is in deck, false otherwise
	 */
	public boolean contains(String card) {
		for (Card current : deck)
		{
			if (current.toString().equalsIgnoreCase(card))
			{
				return true;
			}
		}
		return false;
	}

	public int getVictoryPoints(int gardensPoints) {
		int result = 0;
		for (Card current : deck)
		{
			if (current instanceof Gardens)
			{
				((Gardens) current).setValue(gardensPoints);
			}
			if (current instanceof Points)
			{
				result = result + ((Points) current).getValue();
			}
		}
		return result;
	}

	/**
	 * Check to see if deck can counter an attack card
	 * 
	 * @return true if deck has moat (or other counter), false otherwise
	 */
	public boolean hasCounter() {
		boolean counter = false;
		for (Card current : deck)
		{
			if (current instanceof Action)
			{
				Action temp = (Action) current;
				if (temp.canCounter())
				{
					counter = true;
				}
			}
		}
		return counter;
	}

	/**
	 * Decks are compared only on basis of their top card (to be used for board
	 * decks)
	 * 
	 * @param deck
	 *            - deck to be compared against this one
	 * @return -1 - this deck is less than given deck 0 - decks are equals 1 -
	 *         this deck is greater than given deck
	 */
	public int compareTo(Deck deck) {
		int result;
		if (this.deck.isEmpty() && deck.isEmpty())
		{
			result = 0;
		}
		else if (!this.deck.isEmpty() && deck.isEmpty())
		{
			result = 1;
		}
		else if (this.deck.isEmpty() && !deck.isEmpty())
		{
			result = -1;
		}
		else
		{
			result = revealTopCard().compareTo(deck.revealTopCard());
		}
		return result;
	}
}
